/*
 * @Author: lokei
 * @Date: 2022-08-28 22:46:40
 * @LastEditors: lokei
 * @LastEditTime: 2022-08-30 22:02:13
 * @Description: 
 */
package cn.lokei.gateway.service;

import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import cn.lokei.gateway.entity.IotChannel;
import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

@Component
public class ChannelSupervise implements ApplicationContextAware {

	private static ApplicationContext applicationContext; // Spring应用上下文环境

	// private ChannelGroup globalGroup = new
	// DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
	private ConcurrentMap<String, IotChannel> globalChannel = new ConcurrentHashMap<String, IotChannel>();
	private ConcurrentMap<String, ChannelGroup> subTopicMap = new ConcurrentHashMap<String, ChannelGroup>();

	/*
	 * 实现了ApplicationContextAware 接口，必须实现该方法；
	 * 通过传递applicationContext参数初始化成员变量applicationContext
	 */
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		ChannelSupervise.applicationContext = applicationContext;
	}

	public static ApplicationContext getApplicationContext() {
		return applicationContext;
	}

	@SuppressWarnings("unchecked")
	public static <T> T getBean(String name) throws BeansException {
		return (T) applicationContext.getBean(name);
	}

	public static <T> T getBean(Class<T> clazz) throws BeansException {
		return (T) applicationContext.getBean(clazz);
	}

	public void addChannel(Channel channel) {
		globalChannel.put(channel.id().asLongText(), new IotChannel(channel.id()));
	}

	public void removeChannel(Channel channel) {
		IotChannel iotChannel = this.globalChannel.get(channel.id().asLongText());
		Set<String> subTopics = iotChannel.getSubTopics();
		for (String topic : subTopics) {
			if (this.subTopicMap.get(topic).size() == 0) {
				this.subTopicMap.remove(topic);
			}
		}
		globalChannel.remove(channel.id().asLongText());
	}

	public IotChannel findChannel(String id) {
		return globalChannel.get(id);
	}

	public void subTopic(String topic, Channel channel) {
		ChannelGroup channelGroup = this.subTopicMap.get(topic);
		if (channelGroup == null) {
			channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
			this.subTopicMap.put(topic, channelGroup);
		}
		channelGroup.add(channel);
		IotChannel iotChannel = this.globalChannel.get(channel.id().asLongText());
		iotChannel.getSubTopics().add(topic);
	}

	public ChannelGroup subChannels(String topic) {
		return this.subTopicMap.get(topic);
	}

	public void unsubTopic(List<String> topics, Channel channel) {
		for (String topic : topics) {
			ChannelGroup channelGroup = this.subTopicMap.get(topic);
			if (channelGroup != null) {
				channelGroup.remove(channel);
				if (channelGroup.size() == 0) {
					this.subTopicMap.remove(topic);
				}
			}
		}
	}
}